/*******************************************************************************
 * Copyright (c) 2010 European Software Institute - Tecnalia.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Author - Adri�n Noguero (adrian.noguero@esi.es)
 *     
 *******************************************************************************/

package es.esi.gemde.modelvalidator.ui.dialogs;

import java.util.List;
import java.util.Vector;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

import es.esi.gemde.core.CorePlugin;
import es.esi.gemde.core.resources.ExtendedWizardDialog;
import es.esi.gemde.core.resources.IGemdeResource;
import es.esi.gemde.core.resources.OverlaidImageDescriptor;
import es.esi.gemde.core.resources.IGemdeResource.ResourceType;
import es.esi.gemde.modelvalidator.ModelValidatorPlugin;
import es.esi.gemde.modelvalidator.exceptions.ConstraintCreationException;
import es.esi.gemde.modelvalidator.service.IBatchValidation;
import es.esi.gemde.modelvalidator.service.IConstraintFactory;
import es.esi.gemde.modelvalidator.service.IModelValidatorConstraint;
import es.esi.gemde.modelvalidator.service.IModelValidatorService;
import es.esi.gemde.modelvalidator.service.OCLConstraintFactory;
import es.esi.gemde.modelvalidator.ui.wizards.OCLConstraintCreationWizard;


/**
 * A dialog for managing the Constraint Repository of the Model Validation Service.
 *
 * @author Adri�n Noguero (adrian.noguero@esi.es)
 * @version 1.0
 * @since 1.0
 *
 */
public class ConstraintManagementDialog extends Dialog {

	/* (non-Javadoc)
	 * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
	 */
	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText("GEMDE Constraint Repository Viewer");
		newShell.setImage(CorePlugin.getImage(CorePlugin.CONSTRAINT_REPOSITORY_ICON));
		newShell.setSize(400, 300);
	}

	private TreeViewer constraintViewer;
	private Button createButton;
	private Button editButton;
	private Button removeButton;
	private IModelValidatorService server;
	
	private SelectionListener createButtonListener = new SelectionListener () {

		@Override
		public void widgetDefaultSelected(SelectionEvent e) {
			// Not needed
		}

		@Override
		public void widgetSelected(SelectionEvent e) {
			Object selection = ((StructuredSelection)constraintViewer.getSelection()).getFirstElement();
			OCLConstraintCreationWizard wizard = null;
			if (selection instanceof String) {
				wizard = new OCLConstraintCreationWizard((String)selection);
			}
			else {
				wizard = new OCLConstraintCreationWizard();
			}
			WizardDialog dialog = new ExtendedWizardDialog(getShell(), wizard, CorePlugin.getImage(CorePlugin.CONSTRAINT_ICON, CorePlugin.NEW_OVERLAY, CorePlugin.TOP_RIGHT));
			int status = dialog.open();
			
			if (status == OK) {
				IConstraintFactory factory = new OCLConstraintFactory();
				IModelValidatorConstraint newConstraint = null;
				try {
					newConstraint = factory.createConstraint(wizard.getConstraintName(), wizard.getConstraintCategory(), wizard.getConstraintSeverity(), wizard.getOCL());
				} catch (IllegalArgumentException e1) {
					e1.printStackTrace();
				} catch (ConstraintCreationException e1) {
					e1.printStackTrace();
				}
				if (newConstraint != null) {
					server.addConstraint(newConstraint);
					updateConstraintData();
				}
			}
		}
		
	};
	
	private SelectionListener editButtonListener = new SelectionListener () {

		@Override
		public void widgetDefaultSelected(SelectionEvent e) {
			// Not needed
		}

		@Override
		public void widgetSelected(SelectionEvent e) {
			if (editButton.isEnabled()) {
				Object selection = ((StructuredSelection)constraintViewer.getSelection()).getFirstElement();
				if (selection instanceof IModelValidatorConstraint) {
					
					IModelValidatorConstraint constraint = (IModelValidatorConstraint)selection;
					
					OCLConstraintCreationWizard wizard = new OCLConstraintCreationWizard(constraint.getName(), constraint.getCategory(), constraint.getSeverity(), constraint.getApplicationMetamodel(), constraint.getApplicationContext(), constraint.getBody());
					WizardDialog dialog = new ExtendedWizardDialog(getShell(), wizard, CorePlugin.getImage(CorePlugin.CONSTRAINT_ICON, CorePlugin.NEW_OVERLAY, CorePlugin.TOP_RIGHT));
					int status = dialog.open();
					
					if (status == OK) {
						IConstraintFactory factory = new OCLConstraintFactory();
						IModelValidatorConstraint newConstraint = null;
						try {
							newConstraint = factory.createConstraint(wizard.getConstraintName(), wizard.getConstraintCategory(), wizard.getConstraintSeverity(), wizard.getOCL());
						} catch (IllegalArgumentException e1) {
							e1.printStackTrace();
						} catch (ConstraintCreationException e1) {
							e1.printStackTrace();
						}
						if (newConstraint != null) {
							// We change the constraint with the updated value
							try {
								server.removeConstraint(constraint.getName(), constraint.getCategory());
								server.addConstraint(newConstraint);
							}
							catch (IllegalArgumentException iae) {
								// The constraint is in use in some validation
								boolean res = MessageDialog.openConfirm(getShell(), "Constraint in use!", "The edited constraint is currently being use by one or more validations. Do you still want to update it? (This operation will change the constraint in all validations in which it is referenced)");
								if (res) {
									// Get all the validations containing it! Remove the constraint from them.
									List<IBatchValidation> validations = new Vector<IBatchValidation>();
									for (IBatchValidation val : server.getBatchValidationRepository()) {
										if (val.getSelectedConstraints().contains(selection)) {
											val.getSelectedConstraints().remove(selection);
											server.changeBatchValidation(val);
											validations.add(val);
										}
									}
									// Then try to change the constraint again
									server.removeConstraint(((IModelValidatorConstraint)selection).getName(), ((IModelValidatorConstraint)selection).getCategory());
									server.addConstraint(newConstraint);
									
									// Lastly we include the edited constraint in all the validations from which it was removed
									for (IBatchValidation val : validations) {
										val.getSelectedConstraints().add(newConstraint);
										server.changeBatchValidation(val);
									}
								}
							}
							updateConstraintData();
						}
					}
				}
				
			}
		}
		
	};
	
	private SelectionListener removeButtonListener = new SelectionListener () {

		@Override
		public void widgetDefaultSelected(SelectionEvent e) {
			// Not needed
		}

		@Override
		public void widgetSelected(SelectionEvent e) {
			if (removeButton.isEnabled()) {
				Object selection = ((StructuredSelection)constraintViewer.getSelection()).getFirstElement();
				if (selection instanceof IModelValidatorConstraint) {
					try {
						server.removeConstraint(((IModelValidatorConstraint)selection).getName(), ((IModelValidatorConstraint)selection).getCategory());
					}
					catch (IllegalArgumentException iae) {
						boolean res = MessageDialog.openConfirm(getShell(), "Constraint in use!", "The selected constraint is currently being use by one or more validations. Do you still want to remove it? (This operation will remove the constraint from all validations in which it is referenced)");
						if (res) {
							// Remove the constraint from all the validations containing it!
							List<IBatchValidation> validations = server.getBatchValidationRepository();
							for (IBatchValidation val : validations) {
								if (val.getSelectedConstraints().contains(selection)) {
									val.getSelectedConstraints().remove(selection);
									server.changeBatchValidation(val);
								}
							}
							// Then try to remove the constraint again
							server.removeConstraint(((IModelValidatorConstraint)selection).getName(), ((IModelValidatorConstraint)selection).getCategory());
						}
					}
					updateConstraintData();
				}
			}
		}
		
	};
	
	private ISelectionChangedListener constraintListener = new ISelectionChangedListener() {
		
		@Override
		public void selectionChanged(SelectionChangedEvent event) {
			Object selection = ((StructuredSelection)constraintViewer.getSelection()).getFirstElement();
			if (selection instanceof IModelValidatorConstraint) {
				constraintViewer.getTree().setToolTipText(((IModelValidatorConstraint)selection).getBody());
				if (!((IModelValidatorConstraint)selection).getResourceType().equals(IGemdeResource.ResourceType.NORMAL)) {
					removeButton.setEnabled(false);
					editButton.setEnabled(false);
				}
				else {
					removeButton.setEnabled(true);
					editButton.setEnabled(true);
				}
			}
			else {
				removeButton.setEnabled(false);
				editButton.setEnabled(false);
				constraintViewer.getTree().setToolTipText("");
			}
		}
	};
	
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		// Only create an OK button
		createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Composite topPanel = (Composite)super.createDialogArea(parent);
		
		topPanel.setSize(600, 400);

		GridLayout layout = (GridLayout)topPanel.getLayout();
		layout.numColumns = 4;
		
		constraintViewer = new TreeViewer(topPanel, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
		GridData constraintViewerData = new GridData(GridData.FILL_BOTH);
		constraintViewerData.horizontalSpan = 4;
		constraintViewer.getTree().setLayoutData(constraintViewerData);
		updateConstraintData();
		
		Label emptyLabel = new Label(topPanel, SWT.NONE);
		GridData emptyLabelData = new GridData(GridData.FILL_HORIZONTAL);
		emptyLabelData.horizontalSpan = 1;
		emptyLabel.setText("");
		emptyLabel.setLayoutData(emptyLabelData);
		
		createButton = new Button(topPanel, SWT.PUSH | SWT.CENTER);
		GridData createButtonData = new GridData(GridData.FILL_HORIZONTAL);
		createButtonData.horizontalSpan = 1;
		createButton.setLayoutData(createButtonData);
		createButton.setText("Create Constraint");
		
		editButton = new Button(topPanel, SWT.PUSH | SWT.CENTER);
		GridData editButtonData = new GridData(GridData.FILL_HORIZONTAL);
		editButtonData.horizontalSpan = 1;
		editButton.setLayoutData(editButtonData);
		editButton.setText("Edit Constraint");
		editButton.setEnabled(false);
		
		removeButton = new Button(topPanel, SWT.PUSH | SWT.CENTER);
		GridData removeButtonData = new GridData(GridData.FILL_HORIZONTAL);
		removeButtonData.horizontalSpan = 1;
		removeButton.setLayoutData(removeButtonData);
		removeButton.setText("Remove Constraint");
		removeButton.setEnabled(false);
		
		// Add Listeners
		createButton.addSelectionListener(createButtonListener);
		editButton.addSelectionListener(editButtonListener);
		removeButton.addSelectionListener(removeButtonListener);
		constraintViewer.addSelectionChangedListener(constraintListener);
		
		
		return topPanel;
	}

	/**
	 * Initializes the dialog.
	 *
	 *@param parentShell the parent shell.
	 */
	public ConstraintManagementDialog(Shell parentShell) {
		super(parentShell);
		server = ModelValidatorPlugin.getServer();
	}
	
	/**
	 * Internal method used to update the data of the TreeViewer showing the constraints in the repository
	 */
	private void updateConstraintData () {
		Tree data = constraintViewer.getTree();
		data.removeAll();
		List<String> categories = server.getConstraintCategories();
		List<IModelValidatorConstraint> constraints = server.getConstraintRepository();
		for (String category : categories) {
			TreeItem catItem = new TreeItem(data, SWT.NONE);
			catItem.setText(category);
			catItem.setImage(CorePlugin.getImageDescriptor(CorePlugin.CATEGORY_ICON).createImage());
			catItem.setData(category);
			for (IModelValidatorConstraint constraint : constraints) {
				if (category.equals(constraint.getCategory())) {
					TreeItem constItem = new TreeItem(catItem, SWT.NONE);
					Image icon = null;
					switch (constraint.getSeverity()) {
					case IStatus.INFO:
						icon = CorePlugin.getImage(CorePlugin.CONSTRAINT_ICON, CorePlugin.INFO_OVERLAY, CorePlugin.TOP_LEFT);
						break;
					case IStatus.WARNING:
						icon = CorePlugin.getImage(CorePlugin.CONSTRAINT_ICON, CorePlugin.WARNING_OVERLAY, CorePlugin.TOP_LEFT);
						break;
					case IStatus.ERROR:
						icon = CorePlugin.getImage(CorePlugin.CONSTRAINT_ICON, CorePlugin.ERROR_OVERLAY, CorePlugin.TOP_LEFT);
						break;
					default:
						icon = CorePlugin.getImage(CorePlugin.CONSTRAINT_ICON);
						break;
					}
					if (constraint.getResourceType().equals(ResourceType.PROTECTED)) {
						// Add the lock icon
						icon = new OverlaidImageDescriptor(icon, CorePlugin.getImage(CorePlugin.LOCK_OVERLAY), CorePlugin.BOTTOM_RIGHT).getImage();
					}
					else if (constraint.getResourceType().equals(ResourceType.NETWORKED)) {
						// Add the globe icon
						icon = new OverlaidImageDescriptor(icon, CorePlugin.getImage(CorePlugin.NET_OVERLAY), CorePlugin.BOTTOM_RIGHT).getImage();
					}
					constItem.setText(constraint.getName());
					constItem.setImage(icon);
					constItem.setData(constraint);
				}
			}
		}
		constraintViewer.setSelection(TreeSelection.EMPTY);
	}

}
